package database;

import models.User;
import play.Logger;
import play.db.jpa.JPA;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;

//TODO: impl IQueryExecutor + IQueryExecutorsFactory
public class DBContext implements IDBContext {
    @Override
    public <T> List<T> select(Class<T> c) throws DBException {
        EntityManager entityManager = null;
        try {
            entityManager = JPA.newEntityManager();
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            CriteriaQuery<T> query = criteriaBuilder.createQuery(c);
            CriteriaQuery<T> select = query.select(query.from(c));
            TypedQuery<T> typedQuery = entityManager.createQuery(select);
            return typedQuery.getResultList();
        } catch (Exception e) {
            String message = "Fail to execute select query for class " + c.toString();
            Logger.error(message, e);
            throw new DBException(message);
        } finally {
            if (entityManager != null)
                entityManager.close();
        }
    }

    public List<User> selectUserByLogin(String login) {
        EntityManager entityManager = null;
        try {
            entityManager = JPA.newEntityManager();
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
            Root<User> userTable = query.from(User.class);
            CriteriaQuery<User> select = query.select(userTable).where(criteriaBuilder.equal(userTable.get("login"), login));
            TypedQuery<User> typedQuery = entityManager.createQuery(select);
            return typedQuery.getResultList();
        } catch (Exception e) {
            String message = "Fail to execute select query for user.";
            Logger.error(message, e);
            throw new DBException(message);
        } finally {
            if (entityManager != null)
                entityManager.close();
        }
    }

    @Override
    public <T> void truncateTable(Class<T> c) throws DBException {
        EntityManager entityManager = null;
        EntityTransaction transaction = null;
        try {
            entityManager = JPA.newEntityManager();
            transaction = entityManager.getTransaction();
            transaction.begin();
            Query query = entityManager.createQuery("DELETE FROM " + c.getSimpleName());
            query.executeUpdate();
            transaction.commit();
        } catch (Exception e) {
            String message = "Fail to delete table " + c;
            if (transaction != null)
                transaction.rollback();
            Logger.error(message, e);
            throw new DBException(message);
        } finally {
            if (entityManager != null)
                entityManager.close();
        }
    }

    public <T> void create(T entity) {
        EntityManager entityManager = null;
        EntityTransaction transaction = null;
        try {
            entityManager = JPA.newEntityManager();
            transaction = entityManager.getTransaction();
            transaction.begin();
            entityManager.merge(entity);
            transaction.commit();
        } catch (Exception e) {
            String message = "Fail to create entity";
            Logger.error(message, e);
            if (transaction != null)
                transaction.rollback();
            throw new DBException(message);
        } finally {
            if (entityManager != null)
                entityManager.close();
        }
    }

    public static IDBContext create() {
        try {
            return new DBContext();
        } catch (Throwable e) {
            Logger.error("Fail to create DBContext.", e);
            throw new DBConfigurationException(e);
        }
    }
}
